// src/components/utils.ts

// 确保从 geojson 导入所有需要的类型，并使用 type 关键字进行类型导入
import type {
  Feature,
  LineString,
  Point,
  Polygon,
  Position,
  MultiLineString,
} from "geojson";

// 从 @turf/turf 主包中导入所有需要的函数作为命名导出
import {
  buffer,
  point,
  distance,
  area,
  destination,
  transformScale,
  centroid,
  transformRotate,
  bbox,
  lineString,
  length,
  along,
  booleanValid,
  lineIntersect, // <--- 用于计算线与线的交点
  booleanPointInPolygon, // <--- 用于判断点是否在多边形内
  midpoint, // <--- 用于计算中点
  bearing, // <--- 用于计算航向角
} from "@turf/turf";
import L from "leaflet"; // 导入 Leaflet 以使用 LatLng 类型

// 导入 DebugData 接口
import type { DebugData, MissionType } from "../App"; // 确保导入 MissionType
import type { DroneModel } from "./ObliqueParams"; // 导入 DroneModel

// 定义机场坐标 (移至此处，并导出)
export const AIRPORT_COORDS: L.LatLngTuple = [35.771865, 120.025036]; // [lat, lng]

// 相机参数 (示例值，实际应用中可能需要更精确的相机模型)
export const CAMERA_SENSOR_WIDTH = 13.2; // mm (例如 Mavic 2 Pro)
export const CAMERA_SENSOR_HEIGHT = 8.8; // mm
export const CAMERA_FOCAL_LENGTH = 10.26; // mm (例如 Mavic 2 Pro)
export const CAMERA_IMAGE_WIDTH_PIXELS = 4032; // 图像宽度像素
export const CAMERA_IMAGE_HEIGHT_PIXELS = 3024; // 图像高度像素

// GSD与航高联动系数
// GSD (cm/pixel) = (航高(m) * 100 * 传感器宽度(mm)) / (图像宽度像素 * 焦距(mm))
export const C_FACTOR =
  (100 * CAMERA_SENSOR_WIDTH) /
  (CAMERA_IMAGE_WIDTH_PIXELS * CAMERA_FOCAL_LENGTH);

// Helper function to convert degrees to radians
export function toRadians(degrees: number): number {
  return (degrees * Math.PI) / 180;
}

// Helper function to convert radians to degrees
export function toDegrees(radians: number): number {
  return (radians * 180) / Math.PI; // 修正了这里的转换，应该是 Math.PI
}

// 格式化时长
export function formatDuration(seconds: number): string {
  if (isNaN(seconds) || seconds < 0) return "0s";
  const hours = Math.floor(seconds / 3600);
  const minutes = Math.floor((seconds % 3600) / 60);
  const remainingSeconds = seconds % 60;
  let result = "";
  if (hours > 0) result += `${hours}h`;
  if (minutes > 0) result += `${minutes}min`;
  if (result === "" || remainingSeconds > 0) {
    result += `${remainingSeconds.toFixed(0)}s`;
  }
  return result || "0s";
}

// 格式化面积
export function formatArea(areaSqMeters: number): string {
  if (areaSqMeters >= 1000000) {
    // 大于等于 1 平方公里
    return `${(areaSqMeters / 1000000).toFixed(2)} km²`;
  } else if (areaSqMeters >= 10000) {
    // 大于等于 1 公顷
    return `${(areaSqMeters / 10000).toFixed(2)} 公顷`;
  }
  return `${areaSqMeters.toFixed(2)} m²`;
}

// 定义航线点和拍照点的数据结构
export interface Waypoint {
  lat: number;
  lng: number;
  alt: number; // 海拔高度
}

export interface PhotoPoint {
  lat: number;
  lng: number;
  alt: number;
  heading: number; // 拍照方向 (度)
  tilt: number; // 相机倾斜角 (度, 90为垂直向下)
}

export interface MissionStats {
  duration: number; // 秒
  distance: number; // 米
  photos: number;
  area: number; // 平方米
}

// 定义相机参数接口
export interface CameraParams {
  SENSOR_WIDTH: number;
  SENSOR_HEIGHT: number;
  FOCAL_LENGTH: number;
  IMAGE_WIDTH_PIXELS: number;
  IMAGE_HEIGHT_PIXELS: number;
}

// 新增接口：用于导出时存储任务参数
export interface MissionParamsForExport {
  altitude: number;
  gsd: number;
  frontalOverlap: number;
  sideOverlap: number;
  speed: number;
  angle: number;
  tiltAngle?: number; // 倾斜摄影特有
  buffer?: number; // 倾斜摄影特有
  isSmartObliqueEnabled?: boolean; // 是否为智能摆拍
  droneModel?: DroneModel; // 无人机型号
}

// 定义航线规划结果接口，包含 missionParams
export interface FlightRouteResult {
  waypoints: Waypoint[];
  photoPoints: PhotoPoint[];
  stats: MissionStats;
  error?: string;
  missionParams?: MissionParamsForExport; // 新增：存储用于导出的任务参数
}

// 定义新的返回类型，包含 flightResult 和 debugInfo
export interface GenerateFlightPathOutput {
  flightResult: FlightRouteResult;
  debugInfo: DebugData | null;
}

// Helper function to calculate distance between two LatLng points
function getDistance(p1: Position, p2: Position): number {
  const pt1 = point(p1);
  const pt2 = point(p2);
  return distance(pt1, pt2, { units: "meters" });
}

/**
 * Helper function to generate XML for a single WPML Placemark (waypoint)
 * 严格遵循用户提供的waylines.wpml模板格式
 * @param waypoint The waypoint data
 * @param index The waypoint index (0-based, for actual mission waypoints)
 * @param speed Mission speed (used for waypointSpeed)
 * @param headingAngle Heading angle for the waypoint (0-360 degrees)
 * @param lastWaypointIndex The index of the last *mission* waypoint in the mission (waypoints.length - 1)
 * @param minShootInterval Minimum shoot interval for time-lapse (if applicable, in seconds)
 * @param gimbalPitchAngle Gimbal pitch angle for photo actions (e.g., -90 for vertical, -45 for oblique)
 * @returns XML string for the Placemark
 */
function generateWaypointPlacemarkXml(
  waypoint: Waypoint,
  index: number,
  speed: number,
  headingAngle: number, // -180 to 180 degrees
  lastWaypointIndex: number, // 指的是实际任务航线列表的最后一个索引
  minShootInterval: number | null,
  gimbalPitchAngle: number // Angle in degrees, e.g., -90 for vertical, -45 for oblique
): string {
  // Turn mode logic based on the provided template:
  // First and last *mission* waypoints use "toPointAndStopWithDiscontinuityCurvature" with damping 0
  // Intermediate *mission* waypoints use "coordinateTurn" with damping 0
  const isFirstMissionWaypoint = index === 0;
  const isLastMissionWaypoint = index === lastWaypointIndex;

  const turnMode =
    isFirstMissionWaypoint || isLastMissionWaypoint
      ? "toPointAndStopWithDiscontinuityCurvature"
      : "coordinateTurn";
  const turnDampingDist = 0; // 模板中所有航点都是0

  let actionGroupsXml = "";

  // 拍照动作组应该在第一个任务航点开始
  if (isFirstMissionWaypoint) {
    actionGroupsXml += `
        <wpml:actionGroup>
          <wpml:actionGroupId>0</wpml:actionGroupId>
          <wpml:actionGroupStartIndex>0</wpml:actionGroupStartIndex>
          <wpml:actionGroupEndIndex>${lastWaypointIndex}</wpml:actionGroupEndIndex>
          <wpml:actionGroupMode>sequence</wpml:actionGroupMode>
          <wpml:actionTrigger>
            <wpml:actionTriggerType>betweenAdjacentPoints</wpml:actionTriggerType>
          </wpml:actionTrigger>
          <wpml:action>
            <wpml:actionId>0</wpml:actionId>
            <wpml:actionActuatorFunc>gimbalAngleLock</wpml:actionActuatorFunc>
          </wpml:action>
          <wpml:action>
            <wpml:actionId>1</wpml:actionId>
            <wpml:actionActuatorFunc>gimbalRotate</wpml:actionActuatorFunc>
            <wpml:actionActuatorFuncParam>
              <wpml:gimbalHeadingYawBase>aircraft</wpml:gimbalHeadingYawBase>
              <wpml:gimbalRotateMode>absoluteAngle</wpml:gimbalRotateMode>
              <wpml:gimbalPitchRotateEnable>1</wpml:gimbalPitchRotateEnable>
              <wpml:gimbalPitchRotateAngle>${gimbalPitchAngle.toFixed(
                0
              )}</wpml:gimbalPitchRotateAngle>
              <wpml:gimbalRollRotateEnable>0</wpml:gimbalRollRotateEnable>
              <wpml:gimbalRollRotateAngle>0</wpml:gimbalRollRotateAngle>
              <wpml:gimbalYawRotateEnable>0</wpml:gimbalYawRotateEnable>
              <wpml:gimbalYawRotateAngle>0</wpml:gimbalYawRotateAngle>
              <wpml:gimbalRotateTimeEnable>0</wpml:gimbalRotateTimeEnable>
              <wpml:gimbalRotateTime>10</wpml:gimbalRotateTime>
              <wpml:payloadPositionIndex>0</wpml:payloadPositionIndex>
            </wpml:actionActuatorFuncParam>
          </wpml:action>
          <wpml:action>
            <wpml:actionId>2</wpml:actionId>
            <wpml:actionActuatorFunc>startTimeLapse</wpml:actionActuatorFunc>
            <wpml:actionActuatorFuncParam>
              <wpml:payloadPositionIndex>0</wpml:payloadPositionIndex>
              <wpml:useGlobalPayloadLensIndex>0</wpml:useGlobalPayloadLensIndex>
              <wpml:payloadLensIndex>visable</wpml:payloadLensIndex>
              <wpml:minShootInterval>${
                minShootInterval !== null
                  ? minShootInterval.toFixed(6)
                  : "1.000000"
              }</wpml:minShootInterval>
            </wpml:actionActuatorFuncParam>
          </wpml:action>
        </wpml:actionGroup>
        <wpml:actionGroup>
          <wpml:actionGroupId>1</wpml:actionGroupId>
          <wpml:actionGroupStartIndex>0</wpml:actionGroupStartIndex>
          <wpml:actionGroupEndIndex>${lastWaypointIndex}</wpml:actionGroupEndIndex>
          <wpml:actionGroupMode>sequence</wpml:actionGroupMode>
          <wpml:actionTrigger>
            <wpml:actionTriggerType>multipleTiming</wpml:actionTriggerType>
            <wpml:actionTriggerParam>2</wpml:actionTriggerParam>
          </wpml:actionTrigger>
          <wpml:action>
            <wpml:actionId>0</wpml:actionId>
            <wpml:actionActuatorFunc>gimbalRotate</wpml:actionActuatorFunc>
            <wpml:actionActuatorFuncParam>
              <wpml:gimbalHeadingYawBase>aircraft</wpml:gimbalHeadingYawBase>
              <wpml:gimbalRotateMode>absoluteAngle</wpml:gimbalRotateMode>
              <wpml:gimbalPitchRotateEnable>1</wpml:gimbalPitchRotateEnable>
              <wpml:gimbalPitchRotateAngle>${gimbalPitchAngle.toFixed(
                0
              )}</wpml:gimbalPitchRotateAngle>
              <wpml:gimbalRollRotateEnable>0</wpml:gimbalRollRotateEnable>
              <wpml:gimbalRollRotateAngle>0</wpml:gimbalRollRotateAngle>
              <wpml:gimbalYawRotateEnable>0</wpml:gimbalYawRotateEnable>
              <wpml:gimbalYawRotateAngle>0</wpml:gimbalYawRotateAngle>
              <wpml:gimbalRotateTimeEnable>0</wpml:gimbalRotateTimeEnable>
              <wpml:gimbalRotateTime>10</wpml:gimbalRotateTime>
              <wpml:payloadPositionIndex>0</wpml:payloadPositionIndex>
            </wpml:actionActuatorFuncParam>
          </wpml:action>
        </wpml:actionGroup>`;
  }
  // 在最后一个任务航点结束拍照和解锁云台
  else if (isLastMissionWaypoint) {
    actionGroupsXml += `
        <wpml:actionGroup>
          <wpml:actionGroupId>${lastWaypointIndex * 2}</wpml:actionGroupId>
          <wpml:actionGroupStartIndex>${lastWaypointIndex}</wpml:actionGroupStartIndex>
          <wpml:actionGroupEndIndex>${lastWaypointIndex}</wpml:actionGroupEndIndex>
          <wpml:actionGroupMode>sequence</wpml:actionGroupMode>
          <wpml:actionTrigger>
            <wpml:actionTriggerType>reachPoint</wpml:actionTriggerType>
          </wpml:actionTrigger>
          <wpml:action>
            <wpml:actionId>0</wpml:actionId>
            <wpml:actionActuatorFunc>stopTimeLapse</wpml:actionActuatorFunc>
            <wpml:actionActuatorFuncParam>
              <wpml:payloadPositionIndex>0</wpml:payloadPositionIndex>
              <wpml:payloadLensIndex>visable</wpml:payloadLensIndex>
            </wpml:actionActuatorFuncParam>
          </wpml:action>
          <wpml:action>
            <wpml:actionId>1</wpml:actionId>
            <wpml:actionActuatorFunc>gimbalAngleUnlock</wpml:actionActuatorFunc>
          </wpml:action>
          <wpml:action>
            <wpml:actionId>2</wpml:actionId>
            <wpml:actionActuatorFunc>hover</wpml:actionActuatorFunc>
            <wpml:actionActuatorFuncParam>
              <wpml:hoverTime>0.5</wpml:hoverTime>
            </wpml:actionActuatorFuncParam>
          </wpml:action>
        </wpml:actionGroup>`;
  }

  return `
      <Placemark>
        <Point>
          <coordinates>${waypoint.lng.toFixed(6)},${waypoint.lat.toFixed(
    6
  )}</coordinates>
        </Point>
        <wpml:index>${index}</wpml:index>
        <wpml:executeHeight>${waypoint.alt.toFixed(3)}</wpml:executeHeight>
        <wpml:waypointSpeed>${speed.toFixed(3)}</wpml:waypointSpeed>
        <wpml:waypointHeadingParam>
          <wpml:waypointHeadingMode>followWayline</wpml:waypointHeadingMode>
          <wpml:waypointHeadingAngle>${headingAngle.toFixed(
            6
          )}</wpml:waypointHeadingAngle>
          <wpml:waypointPoiPoint>0.000000,0.000000,0.000000</wpml:waypointPoiPoint>
          <wpml:waypointHeadingAngleEnable>1</wpml:waypointHeadingAngleEnable>
          <wpml:waypointHeadingPathMode>followBadArc</wpml:waypointHeadingPathMode>
          <wpml:waypointHeadingPoiIndex>0</wpml:waypointHeadingPoiIndex>
        </wpml:waypointHeadingParam>
        <wpml:waypointTurnParam>
          <wpml:waypointTurnMode>${turnMode}</wpml:waypointTurnMode>
          <wpml:waypointTurnDampingDist>${turnDampingDist.toFixed(
            2
          )}</wpml:waypointTurnDampingDist>
        </wpml:waypointTurnParam>
        <wpml:useStraightLine>1</wpml:useStraightLine>
        ${actionGroupsXml}
        <wpml:waypointGimbalHeadingParam>
          <wpml:waypointGimbalPitchAngle>0</wpml:waypointGimbalPitchAngle>
          <wpml:waypointGimbalYawAngle>0</wpml:waypointGimbalYawAngle>
        </wpml:waypointGimbalHeadingParam>
        <wpml:isRisky>0</wpml:isRisky>
        <wpml:waypointWorkType>0</wpml:waypointWorkType>
      </Placemark>`;
}

/**
 * 辅助函数：将一条线段的航点和拍照点添加到总路径中
 * @param line GeoJSON LineString，表示一条扫描线段
 * @param reverse 是否反向遍历线段 (true: end->start, false: start->end)
 * @param currentAltitude 航点高度
 * @param currentAngle 航向角
 * @param currentTilt 相机俯仰角
 * @param photoIntervalDistance 拍照点间距
 * @returns 包含航点、拍照点和线段距离的对象
 */
const generateSegmentPoints = (
  line: Feature<LineString>,
  reverse: boolean,
  currentAltitude: number,
  currentAngle: number,
  currentTilt: number,
  photoIntervalDistance: number
): {
  segmentWaypoints: Waypoint[];
  segmentPhotoPoints: PhotoPoint[];
  segmentDistance: number;
} => {
  let segmentWaypoints: Waypoint[] = [];
  let segmentPhotoPoints: PhotoPoint[] = [];
  let segmentDistance = 0;
  let currentSegmentPos: Position | null = null;

  let lineCoords = [...line.geometry.coordinates];
  if (reverse) {
    lineCoords.reverse();
  }

  // 严格验证 lineCoords 的有效性，防止 turf.js 报错
  const isValidLineCoords =
    Array.isArray(lineCoords) &&
    lineCoords.length >= 2 &&
    lineCoords.every(
      (coord) =>
        Array.isArray(coord) &&
        coord.length === 2 &&
        typeof coord[0] === "number" &&
        !isNaN(coord[0]) &&
        typeof coord[1] === "number" &&
        !isNaN(coord[1])
    );

  if (!isValidLineCoords) {
    console.error(
      "generateSegmentPoints: Invalid or malformed lineCoords detected, skipping segment:",
      lineCoords,
      line
    );
    return { segmentWaypoints: [], segmentPhotoPoints: [], segmentDistance: 0 };
  }

  // 现在我们确信 lineCoords 是一个有效的 [number, number] 数组
  const turfLine = lineString(lineCoords);
  const lineLen = length(turfLine, { units: "meters" });

  const stepDistance = photoIntervalDistance > 0 ? photoIntervalDistance : 5; // 航点/拍照点密度，至少5米

  // 处理非常短的线段，只添加起点
  if (lineLen < stepDistance / 2) {
    const p = lineCoords[0]; // 此时 lineCoords 至少有2个点，所以 lineCoords[0] 是安全的
    segmentWaypoints.push({ lat: p[1], lng: p[0], alt: currentAltitude });
    segmentPhotoPoints.push({
      lat: p[1],
      lng: p[0],
      alt: currentAltitude,
      heading: currentAngle,
      tilt: currentTilt,
    });
    return { segmentWaypoints, segmentPhotoPoints, segmentDistance: lineLen };
  }

  // 遍历线段生成航点和拍照点
  for (let d = 0; d <= lineLen + 0.01; d += stepDistance) {
    // +0.01 确保能取到最后一个点
    const pointAlongLine = along(turfLine, Math.min(d, lineLen), {
      units: "meters",
    });
    const currentPointPos: Position = pointAlongLine.geometry.coordinates;

    // 再次验证 along 返回的点的有效性
    if (
      !Array.isArray(currentPointPos) ||
      currentPointPos.length !== 2 ||
      typeof currentPointPos[0] !== "number" ||
      isNaN(currentPointPos[0]) ||
      typeof currentPointPos[1] !== "number" ||
      isNaN(currentPointPos[1])
    ) {
      console.warn(
        "generateSegmentPoints: 'along' returned invalid coordinates, skipping point:",
        pointAlongLine
      );
      continue; // 跳过此点
    }

    if (
      currentSegmentPos &&
      getDistance(currentPointPos, currentSegmentPos) > 0.1
    ) {
      segmentDistance += getDistance(currentSegmentPos, currentPointPos);
    }
    segmentWaypoints.push({
      lat: currentPointPos[1],
      lng: currentPointPos[0],
      alt: currentAltitude,
    });
    segmentPhotoPoints.push({
      lat: currentPointPos[1],
      lng: currentPointPos[0],
      alt: currentAltitude,
      heading: currentAngle,
      tilt: currentTilt,
    });
    currentSegmentPos = currentPointPos;
  }
  return { segmentWaypoints, segmentPhotoPoints, segmentDistance };
};

export function generateMappingFlightPath(
  polygonGeoJSON: Feature<Polygon>,
  altitude: number,
  gsd: number,
  frontalOverlap: number,
  sideOverlap: number,
  speed: number,
  angle: number,
  cameraParams: CameraParams,
  takeoffPoint: L.LatLng // 起飞点作为参考点
): GenerateFlightPathOutput {
  console.log("--- 开始生成建图航拍航线 ---");
  console.log("输入参数:", {
    altitude,
    gsd,
    frontalOverlap,
    sideOverlap,
    speed,
    angle,
    cameraParams,
    takeoffPoint,
  });
  console.log("原始多边形 (GeoJSON):", JSON.stringify(polygonGeoJSON));

  // --- 调试数据收集 ---
  const debugMidPoints: Feature<Point>[] = [];
  const debugMidPointsInPolygon: Feature<Point>[] = [];
  let rotatedPolygonForScan: Feature<Polygon> | null = null;
  let bufferedPolygonForScan: Feature<Polygon> | null = null;
  const scanLines: Feature<LineString>[] = [];
  // --- 调试数据收集 ---

  const defaultErrorResult = (errorMessage: string) => ({
    flightResult: {
      waypoints: [], // 不包含起飞点
      photoPoints: [],
      stats: { duration: 0, distance: 0, photos: 0, area: 0 },
      error: errorMessage,
    },
    debugInfo: null,
  });

  // 0. 验证输入多边形
  if (!booleanValid(polygonGeoJSON)) {
    console.error("错误: 输入多边形无效。", polygonGeoJSON);
    return defaultErrorResult("输入的多边形无效，请检查绘制区域。");
  }

  // 1. & 2. 参数计算
  const pixelGroundSize = gsd / 100; // m/pixel
  const imageWidthGround = pixelGroundSize * cameraParams.IMAGE_WIDTH_PIXELS; // m
  const imageHeightGround = pixelGroundSize * cameraParams.IMAGE_HEIGHT_PIXELS; // m
  const lineSpacing = imageWidthGround * (1 - sideOverlap); // 航线间距
  const photoIntervalDistance = imageHeightGround * (1 - frontalOverlap); // 拍照点间距

  console.log("计算出的地面照片尺寸:", { imageWidthGround, imageHeightGround });
  console.log(
    "计算出的航线间距:",
    lineSpacing,
    "拍照点间距:",
    photoIntervalDistance
  );

  if (lineSpacing <= 0.1 || photoIntervalDistance <= 0.1) {
    // 增加一个小的阈值，避免除以零或负数
    console.error("错误: 重叠度设置过高导致航线或拍照点间距为负或零。");
    return defaultErrorResult(
      "重叠度设置过高或航高/GSD不合理，导致航线或拍照点间距过小。"
    );
  }

  // 3. & 4. 多边形和扫描线生成 (使用旋转边界框)
  const totalArea = area(polygonGeoJSON);
  console.log("多边形面积:", totalArea);

  const centroidPoint = centroid(polygonGeoJSON);
  console.log("多边形中心点:", JSON.stringify(centroidPoint));

  const targetPolygon = polygonGeoJSON;

  // 计算旋转角度，使航线方向与X轴平行 (0度正北，顺时针，所以 90-angle)
  // Turf.js 的 transformRotate 顺时针为正，所以 90 - angle 是正确的。
  const rotationAngle = 90 - angle;
  console.log("用于旋转多边形的角度 (顺时针):", rotationAngle);

  rotatedPolygonForScan = transformRotate(targetPolygon, rotationAngle, {
    pivot: centroidPoint,
  });
  if (!booleanValid(rotatedPolygonForScan)) {
    console.error("错误: 旋转后的多边形无效！", rotatedPolygonForScan);
    return defaultErrorResult("旋转后的区域无效，请检查原始区域。");
  }
  console.log(
    "旋转后的多边形 (rotatedPolygonForScan):",
    JSON.stringify(rotatedPolygonForScan)
  );

  // 计算一个合适的缓冲区距离，用于确保扫描线生成范围覆盖到边缘
  // 例如，半个图像宽度，或者至少5米
  const scanBufferMeters = Math.max(imageWidthGround / 2, 5);

  // 对旋转后的多边形进行缓冲，以确保扫描线生成范围覆盖到边缘
  bufferedPolygonForScan = buffer(rotatedPolygonForScan, scanBufferMeters, {
    units: "meters",
  });
  if (!bufferedPolygonForScan || !booleanValid(bufferedPolygonForScan)) {
    console.error("错误: 缓冲多边形生成失败或无效！", bufferedPolygonForScan);
    return defaultErrorResult("缓冲区域无效，请检查原始区域。");
  }
  console.log(
    "创建了缓冲多边形用于判断 (bufferedPolygonForScan):",
    JSON.stringify(bufferedPolygonForScan?.geometry?.coordinates)
  );

  const bboxBufferedRotated = bbox(bufferedPolygonForScan);
  console.log("缓冲旋转后多边形的BBox (用于扫描):", bboxBufferedRotated);

  const minXRotated = bboxBufferedRotated[0];
  const minYScaled = bboxBufferedRotated[1];
  const maxXRotated = bboxBufferedRotated[2];
  const maxYScaled = bboxBufferedRotated[3];

  // 生成扫描线 (在旋转后的缓冲多边形BBox内)
  let currentY = minYScaled;
  // 计算纬度增量，以米为单位的线间距转换为地理坐标的增量
  // 使用 destination 函数更精确地计算1米对应的纬度变化量
  const testPointAtCurrentY = point([
    centroidPoint.geometry.coordinates[0],
    currentY,
  ]);
  const pointOneMeterNorth = destination(testPointAtCurrentY, 1, 0, {
    units: "meters",
  }); // 沿正北方向1米
  const latPerMeter = Math.abs(
    pointOneMeterNorth.geometry.coordinates[1] -
      testPointAtCurrentY.geometry.coordinates[1]
  );

  if (latPerMeter === 0) {
    // 避免除以零
    console.error("错误: 无法计算纬度/米转换率，可能地图中心点有问题。");
    return defaultErrorResult("无法计算地理转换率，请检查地图区域。");
  }

  while (currentY <= maxYScaled + lineSpacing * latPerMeter * 0.5) {
    // 稍微增加范围以确保包含边界
    const lineCoords: Position[] = [];
    lineCoords.push([minXRotated, currentY]);
    lineCoords.push([maxXRotated, currentY]);
    scanLines.push(lineString(lineCoords));
    currentY += lineSpacing * latPerMeter; // 将米转换为纬度增量
  }
  console.log("生成的扫描线数量:", scanLines.length);

  // 5. 裁剪航线到多边形内部 (使用旋转后的原始多边形进行裁剪)
  const clippedLinesInRotatedSpace: Feature<LineString>[] = [];
  const polygonOuterRingRotated = rotatedPolygonForScan.geometry.coordinates[0];

  scanLines.forEach((line, lineIndex) => {
    const intersections: Position[] = [];
    // 寻找扫描线与旋转后原始多边形边界的交点
    for (let i = 0; i < polygonOuterRingRotated.length - 1; i++) {
      const p1 = polygonOuterRingRotated[i];
      const p2 = polygonOuterRingRotated[i + 1];
      const edge = lineString([p1, p2]);

      const intersectResult = lineIntersect(line, edge);
      if (intersectResult.features.length > 0) {
        intersectResult.features.forEach((feat) => {
          // 验证交点坐标的有效性
          if (
            Array.isArray(feat.geometry.coordinates) &&
            feat.geometry.coordinates.length === 2 &&
            typeof feat.geometry.coordinates[0] === "number" &&
            !isNaN(feat.geometry.coordinates[0]) &&
            typeof feat.geometry.coordinates[1] === "number" &&
            !isNaN(feat.geometry.coordinates[1])
          ) {
            intersections.push(feat.geometry.coordinates);
          } else {
            console.warn("Invalid intersection point found:", feat);
          }
        });
      }
    }

    // 将扫描线的起点、终点和所有交点合并，并按X坐标排序
    const allPointsOnLine: Position[] = [
      line.geometry.coordinates[0],
      line.geometry.coordinates[1],
      ...intersections,
    ];
    allPointsOnLine.sort((a, b) => {
      if (a[0] !== b[0]) return a[0] - b[0];
      return a[1] - b[1];
    });

    // 去除重复点并验证点有效性
    const uniquePointsOnLine: Position[] = [];
    if (allPointsOnLine.length > 0) {
      if (
        Array.isArray(allPointsOnLine[0]) &&
        typeof allPointsOnLine[0][0] === "number" &&
        !isNaN(allPointsOnLine[0][0])
      ) {
        uniquePointsOnLine.push(allPointsOnLine[0]);
      }
      for (let i = 1; i < allPointsOnLine.length; i++) {
        const prev = uniquePointsOnLine[uniquePointsOnLine.length - 1];
        const curr = allPointsOnLine[i];
        // 验证当前点的有效性
        if (
          !Array.isArray(curr) ||
          curr.length !== 2 ||
          typeof curr[0] !== "number" ||
          isNaN(curr[0]) ||
          typeof curr[1] !== "number" ||
          isNaN(curr[1])
        ) {
          console.warn("Skipping invalid point in allPointsOnLine:", curr);
          continue;
        }
        if (getDistance(prev, curr) > 0.01) {
          // 0.01米阈值
          uniquePointsOnLine.push(curr);
        }
      }
    }

    // 检查每个线段的中心点是否在原始旋转多边形内部，以确定是否保留该线段
    for (let i = 0; i < uniquePointsOnLine.length - 1; i++) {
      const startPos = uniquePointsOnLine[i];
      const endPos = uniquePointsOnLine[i + 1];

      // 再次验证 startPos 和 endPos 的有效性
      if (
        !Array.isArray(startPos) ||
        startPos.length !== 2 ||
        typeof startPos[0] !== "number" ||
        isNaN(startPos[0]) ||
        typeof startPos[1] !== "number" ||
        isNaN(startPos[1]) ||
        !Array.isArray(endPos) ||
        endPos.length !== 2 ||
        typeof endPos[0] !== "number" ||
        isNaN(endPos[0]) ||
        typeof endPos[1] !== "number" ||
        isNaN(endPos[1])
      ) {
        console.warn(
          "Skipping segment due to invalid start/end position:",
          startPos,
          endPos
        );
        continue;
      }

      if (getDistance(startPos, endPos) < 0.1) continue; // 忽略过短的线段

      const mid = midpoint(point(startPos), point(endPos));
      // 验证 mid 点的有效性
      if (
        !mid ||
        !Array.isArray(mid.geometry.coordinates) ||
        mid.geometry.coordinates.length !== 2 ||
        typeof mid.geometry.coordinates[0] !== "number" ||
        isNaN(mid.geometry.coordinates[0]) ||
        typeof mid.geometry.coordinates[1] !== "number" ||
        isNaN(mid.geometry.coordinates[1])
      ) {
        console.warn("Skipping segment due to invalid midpoint:", mid);
        continue;
      }

      debugMidPoints.push(mid);
      const isInPolygon = booleanPointInPolygon(mid, rotatedPolygonForScan); // 使用原始旋转后的多边形进行最终的裁剪判断
      if (isInPolygon) {
        clippedLinesInRotatedSpace.push(lineString([startPos, endPos]));
        debugMidPointsInPolygon.push(mid);
      }
    }
  });
  console.log(
    "裁剪后的线段数量 (clippedLinesInRotatedSpace):",
    clippedLinesInRotatedSpace.length
  );

  // 6. 将裁剪后的航线段旋转回原始角度
  const finalLines = clippedLinesInRotatedSpace.map((line) =>
    transformRotate(line, -rotationAngle, { pivot: centroidPoint })
  );
  console.log("最终航线段数量 (finalLines):", finalLines.length);

  // 7. & 8. 整合航线、生成航点和统计信息
  const flightResult: FlightRouteResult = {
    waypoints: [], // 不包含起飞点
    photoPoints: [],
    stats: { duration: 0, distance: 0, photos: 0, area: totalArea },
    error: "区域过小或参数不合理，未能生成有效航线。",
  };

  if (finalLines.length === 0) {
    console.error("错误: finalLines 为空，未能生成有效航线。");
    return defaultErrorResult("未能生成有效航线，请检查区域大小或参数。");
  }

  // --- 航线优化开始：S形路径和最近起飞点逻辑 ---

  // 定义起飞点位置，用于排序和距离计算
  const takeoffPointPosition: Position = [takeoffPoint.lng, takeoffPoint.lat];
  console.log("DEBUG: Takeoff Point (Lng, Lat):", takeoffPointPosition);

  // 1. 确保 finalLines 按照距离起飞点的最小距离进行排序
  // 这样可以确保S形路径从距离起飞点最近的线段开始
  finalLines.sort((lineA, lineB) => {
    // 计算 lineA 到起飞点的最小距离
    const startPosA = lineA.geometry.coordinates[0];
    const endPosA =
      lineA.geometry.coordinates[lineA.geometry.coordinates.length - 1];
    const distToStartA = getDistance(takeoffPointPosition, startPosA);
    const distToEndA = getDistance(takeoffPointPosition, endPosA);
    const minDistanceA = Math.min(distToStartA, distToEndA);

    // 计算 lineB 到起飞点的最小距离
    const startPosB = lineB.geometry.coordinates[0];
    const endPosB =
      lineB.geometry.coordinates[lineB.geometry.coordinates.length - 1];
    const distToStartB = getDistance(takeoffPointPosition, startPosB);
    const distToEndB = getDistance(takeoffPointPosition, endPosB);
    const minDistanceB = Math.min(distToStartB, distToEndB);

    return minDistanceA - minDistanceB;
  });
  console.log("DEBUG: finalLines sorted by closest distance to takeoff point.");

  // 2. 确定任务的实际入口点 (离起飞点最近的扫描线起点或终点)
  let missionEntryWaypoint: Waypoint | null = null;
  let minDistanceToTakeoff = Infinity; // 初始化为无穷大，用于寻找最小值
  let initialLineIndex = -1; // 存储距离起飞点最近的线段在 *已排序* 的 `finalLines` 数组中的索引
  // `initialLineTraversalStartsAtStart`: 布尔值，如果为 `true`，表示最近点是该线段的“自然起点”（即 `line.geometry.coordinates[0]`）；如果为 `false`，表示最近点是该线段的“自然终点”（即 `line.geometry.coordinates[line.length - 1]`）。
  let initialLineTraversalStartsAtStart = false;

  console.log(
    "DEBUG: --- Starting closest point search (on sorted finalLines) ---"
  );

  // **遍历已排序的 `finalLines` 的起点和终点，寻找距离 `takeoffPoint` 最近的点**
  // 由于 `finalLines` 已经按距离排序，理论上第一个线段的端点就会是最近的。
  // 但为了鲁棒性，仍然遍历所有端点。
  finalLines.forEach((line, index) => {
    // 确保线段本身有有效的坐标
    if (
      !line ||
      !Array.isArray(line.geometry.coordinates) ||
      line.geometry.coordinates.length < 2
    ) {
      console.warn(
        `DEBUG: Skipping invalid line ${index} in closest point search:`,
        line
      );
      return;
    }

    const startPos = line.geometry.coordinates[0]; // 获取线段的第一个点
    const endPos =
      line.geometry.coordinates[line.geometry.coordinates.length - 1]; // 获取线段的最后一个点

    // **验证 `startPos` 和 `endPos` 的有效性（非常重要）**
    if (
      !Array.isArray(startPos) ||
      startPos.length !== 2 ||
      typeof startPos[0] !== "number" ||
      isNaN(startPos[0]) ||
      typeof startPos[1] !== "number" ||
      isNaN(startPos[1])
    ) {
      console.warn(
        `DEBUG: Skipping invalid startPos for line ${index}:`,
        startPos
      );
      return;
    }
    if (
      !Array.isArray(endPos) ||
      endPos.length !== 2 ||
      typeof endPos[0] !== "number" ||
      isNaN(endPos[0]) ||
      typeof endPos[1] !== "number" ||
      isNaN(endPos[1])
    ) {
      console.warn(`DEBUG: Skipping invalid endPos for line ${index}:`, endPos);
      return;
    }

    // **检查当前线段的起点到起飞点的距离**
    const distToStart = getDistance(takeoffPointPosition, startPos);
    console.log(
      `DEBUG: Line ${index} Start Pos (${startPos[0].toFixed(
        5
      )}, ${startPos[1].toFixed(5)}), Dist to Takeoff: ${distToStart.toFixed(
        2
      )}m`
    );
    if (distToStart < minDistanceToTakeoff) {
      minDistanceToTakeoff = distToStart; // 更新最小距离
      missionEntryWaypoint = {
        lat: startPos[1],
        lng: startPos[0],
        alt: altitude,
      }; // 更新最近的航点
      initialLineIndex = index; // 更新最近点所属线段的索引
      initialLineTraversalStartsAtStart = true; // 标记最近点是该线段的起点
      console.log(
        `DEBUG:   -> New closest: Line ${index} Start, Distance: ${minDistanceToTakeoff.toFixed(
          2
        )}m`
      );
    }

    // **检查当前线段的终点到起飞点的距离**
    const distToEnd = getDistance(takeoffPointPosition, endPos);
    console.log(
      `DEBUG: Line ${index} End Pos (${endPos[0].toFixed(
        5
      )}, ${endPos[1].toFixed(5)}), Dist to Takeoff: ${distToEnd.toFixed(2)}m`
    );
    if (distToEnd < minDistanceToTakeoff) {
      // 注意这里是 < minDistanceToTakeoff，如果距离相等，则不更新，保持之前的选择
      minDistanceToTakeoff = distToEnd; // 更新最小距离
      missionEntryWaypoint = { lat: endPos[1], lng: endPos[0], alt: altitude }; // 更新最近的航点
      initialLineIndex = index; // 更新最近点所属线段的索引
      initialLineTraversalStartsAtStart = false; // 标记最近点是该线段的终点
      console.log(
        `DEBUG:   -> New closest: Line ${index} End, Distance: ${minDistanceToTakeoff.toFixed(
          2
        )}m`
      );
    }
  });
  console.log("DEBUG: --- Closest point search finished ---");
  console.log("DEBUG: Final Mission Entry Waypoint:", missionEntryWaypoint);
  console.log("DEBUG: Initial Line Index:", initialLineIndex);
  console.log(
    "DEBUG: Initial Line Traversal Starts At Start:",
    initialLineTraversalStartsAtStart
  );

  if (!missionEntryWaypoint) {
    console.error("Error: 无法确定任务入口航点。", {
      takeoffPointPosition,
      finalLines,
    });
    return defaultErrorResult("无法确定任务入口航点，请检查起飞点和区域。");
  }

  let missionWaypoints: Waypoint[] = [];
  let missionPhotoPoints: PhotoPoint[] = [];
  let totalMissionFlightDistance = 0; // 仅累加任务区域内的飞行距离

  // `lastWaypointForDistance` 现在从 `missionEntryWaypoint` 开始，因为起飞点不是任务航线的一部分
  let lastWaypointForDistance: Position = [
    missionEntryWaypoint!.lng,
    missionEntryWaypoint!.lat,
  ];
  missionWaypoints.push(missionEntryWaypoint!); // 将最近的入口点作为第一个任务航点

  // 3. 构建 S 形路径
  // `initialLineReverseFlag` 决定了 `finalLines[initialLineIndex]` 在 `generateSegmentPoints` 中是否需要反向。
  // 如果 `initialLineTraversalStartsAtStart` 为 `true` (最近点是线段的起点)，那么该线段在 `generateSegmentPoints` 中不需要反向 (`reverse: false`)。所以 `initialLineReverseFlag` 应该为 `false`。
  // 如果 `initialLineTraversalStartsAtStart` 为 `false` (最近点是线段的终点)，那么该线段在 `generateSegmentPoints` 中需要反向 (`reverse: true`)。所以 `initialLineReverseFlag` 应该为 `true`。
  // 简而言之，`initialLineReverseFlag` 的值与 `initialLineTraversalStartsAtStart` 的值是相反的。
  let initialLineReverseFlag = !initialLineTraversalStartsAtStart;

  // 存储所有线段的有序路径，包含其正确的反向标志
  const orderedMissionLines: { line: Feature<LineString>; reverse: boolean }[] =
    [];

  // **S形逻辑核心：** 遍历所有排序后的 `finalLines`，为每条线段确定其遍历方向。
  for (let i = 0; i < finalLines.length; i++) {
    let reverseThisLine: boolean;
    if (i === initialLineIndex) {
      // 如果是距离起飞点最近的线段，使用计算出的初始方向标志
      reverseThisLine = initialLineReverseFlag;
    } else {
      // 对于其他线段，其遍历方向与起始线段的方向交替。
      // 如果当前线段的索引 `i` 与起始线段的索引 `initialLineIndex` 的差值是偶数，
      // 则它们的方向相同；如果是奇数，则方向相反。
      if (Math.abs(i - initialLineIndex) % 2 === 0) {
        reverseThisLine = initialLineReverseFlag; // 偶数差值，方向与起始线段相同
      } else {
        reverseThisLine = !initialLineReverseFlag; // 奇数差值，方向与起始线段相反
      }
    }
    orderedMissionLines.push({ line: finalLines[i], reverse: reverseThisLine });
  }
  // 这一步结束后，`orderedMissionLines` 数组中包含了所有航线段，并且每个线段都带有一个 `reverse` 标志，指示了它应该如何被遍历，从而形成一个完整的S形路径。

  // 现在根据 `orderedMissionLines` 生成最终的航点
  for (let i = 0; i < orderedMissionLines.length; i++) {
    const { line, reverse } = orderedMissionLines[i];
    const { segmentWaypoints, segmentPhotoPoints, segmentDistance } =
      generateSegmentPoints(
        line,
        reverse,
        altitude,
        angle,
        90,
        photoIntervalDistance
      ); // 调用辅助函数生成线段内部的航点

    // 添加连接线（如果需要），从上一个航点连接到当前线段的第一个点
    // 只有当当前线段不是 `initialLineIndex` 且 `missionWaypoints` 不为空时才添加连接线
    // 并且连接线不应是任务飞行距离的一部分，只在航线段之间计算距离
    if (
      i !== initialLineIndex && // 避免重复添加 initialLineIndex 对应的 segmentWaypoints[0]
      segmentWaypoints.length > 0 &&
      getDistance(lastWaypointForDistance, [
        segmentWaypoints[0].lng,
        segmentWaypoints[0].lat,
      ]) > 0.1
    ) {
      totalMissionFlightDistance += getDistance(lastWaypointForDistance, [
        segmentWaypoints[0].lng,
        segmentWaypoints[0].lat,
      ]);
      missionWaypoints.push(segmentWaypoints[0]);
      lastWaypointForDistance = [
        segmentWaypoints[0].lng,
        segmentWaypoints[0].lat,
      ];
    } else if (i === initialLineIndex && missionWaypoints.length > 0) {
      // 对于 initialLineIndex，如果其第一个点与 missionEntryWaypoint 不同，也需要计算距离
      if (
        getDistance(lastWaypointForDistance, [
          segmentWaypoints[0].lng,
          segmentWaypoints[0].lat,
        ]) > 0.1
      ) {
        totalMissionFlightDistance += getDistance(lastWaypointForDistance, [
          segmentWaypoints[0].lng,
          segmentWaypoints[0].lat,
        ]);
        missionWaypoints.push(segmentWaypoints[0]);
        lastWaypointForDistance = [
          segmentWaypoints[0].lng,
          segmentWaypoints[0].lat,
        ];
      }
    }

    // 添加线段内部的航点和拍照点
    // 注意：如果 i === initialLineIndex，segmentWaypoints[0] 已经作为 missionEntryWaypoint 添加
    // 或者作为连接点添加了，所以这里从 slice(1) 开始，但要确保 segmentWaypoints 长度足够
    const pointsToAdd =
      i === initialLineIndex &&
      missionWaypoints.length > 0 &&
      getDistance(
        [
          missionWaypoints[missionWaypoints.length - 1].lng,
          missionWaypoints[missionWaypoints.length - 1].lat,
        ],
        [segmentWaypoints[0].lng, segmentWaypoints[0].lat]
      ) < 0.1
        ? segmentWaypoints.slice(1)
        : segmentWaypoints;

    missionWaypoints.push(...pointsToAdd);
    missionPhotoPoints.push(...segmentPhotoPoints);
    totalMissionFlightDistance += segmentDistance; // 累加线段内部距离
    if (segmentWaypoints.length > 0) {
      lastWaypointForDistance = [
        segmentWaypoints[segmentWaypoints.length - 1].lng,
        segmentWaypoints[segmentWaypoints.length - 1].lat,
      ];
    }
  }

  // 最终去重：移除所有连续的、距离过近的航点
  const finalWaypoints: Waypoint[] = [];
  if (missionWaypoints.length > 0) {
    finalWaypoints.push(missionWaypoints[0]);
    for (let i = 1; i < missionWaypoints.length; i++) {
      const prev = finalWaypoints[finalWaypoints.length - 1];
      const curr = missionWaypoints[i];
      // 验证当前点的有效性
      if (
        !curr ||
        !Array.isArray([curr.lng, curr.lat]) ||
        typeof curr.lng !== "number" ||
        isNaN(curr.lng) ||
        typeof curr.lat !== "number" ||
        isNaN(curr.lat)
      ) {
        console.warn("Skipping invalid waypoint during deduplication:", curr);
        continue;
      }
      if (getDistance([prev.lng, prev.lat], [curr.lng, curr.lat]) > 0.1) {
        finalWaypoints.push(curr);
      }
    }
  }

  // 重新计算总任务飞行距离基于去重后的航点
  let recalculatedTotalMissionFlightDistance = 0;
  for (let i = 0; i < finalWaypoints.length - 1; i++) {
    recalculatedTotalMissionFlightDistance += getDistance(
      [finalWaypoints[i].lng, finalWaypoints[i].lat],
      [finalWaypoints[i + 1].lng, finalWaypoints[i + 1].lat]
    );
  }

  // 计算往返起飞点的距离 (如果任务航点存在)
  let distanceToFirstMissionWaypoint = 0;
  let distanceFromLastMissionWaypoint = 0;
  if (finalWaypoints.length > 0) {
    distanceToFirstMissionWaypoint = getDistance(takeoffPointPosition, [
      finalWaypoints[0].lng,
      finalWaypoints[0].lat,
    ]);
    distanceFromLastMissionWaypoint = getDistance(
      [
        finalWaypoints[finalWaypoints.length - 1].lng,
        finalWaypoints[finalWaypoints.length - 1].lat,
      ],
      takeoffPointPosition
    );
  }

  // 计算总飞行距离 (包含往返起飞点)
  const totalFlightDistanceIncludingTransit =
    distanceToFirstMissionWaypoint +
    recalculatedTotalMissionFlightDistance +
    distanceFromLastMissionWaypoint;

  // 填充 `flightResult` 对象
  flightResult.waypoints = finalWaypoints; // 仅任务航点
  flightResult.photoPoints = missionPhotoPoints;
  flightResult.stats.duration =
    speed > 0 ? totalFlightDistanceIncludingTransit / speed : 0; // 总时长
  flightResult.stats.distance = totalFlightDistanceIncludingTransit; // 总距离
  flightResult.stats.photos = missionPhotoPoints.length;
  flightResult.stats.area = totalArea;
  flightResult.error = undefined;

  return {
    flightResult: flightResult,
    debugInfo: {
      rotatedPolygonForScan: rotatedPolygonForScan,
      bufferedPolygonForScan: bufferedPolygonForScan,
      scanLines: scanLines,
      midPoints: debugMidPoints,
      midPointsInPolygon: debugMidPointsInPolygon,
    },
  };
}

/**
 * 生成倾斜摄影的航线和拍照点
 * @param {Feature<Polygon>} polygonGeoJSON - 飞行区域的GeoJSON多边形
 * @param {number} altitude - 航高 (m)
 * @param {number} gsd - 地面分辨率 (cm/pixel) (通常指垂直GSD)
 * @param {number} frontalOverlap - 航向重叠度 (0-1)
 * @param {number} sideOverlap - 旁向重叠度 (0-1)
 * @param {number} speed - 飞行速度 (m/s)
 * @param {number} angle - 航线方向角度 (度, 0-180, 0度为正北)
 * @param {number} tiltAngle - 相机倾斜角度 (度, 与地面夹角, 90度为垂直)
 * @param {number} bufferDistance - 缓冲区距离 (m)
 * @param {CameraParams} cameraParams - 相机参数
 * @param {L.LatLng} takeoffPoint - 起飞点 (新增参数，确保不为null)
 * @param {boolean} isSmartObliqueEnabled - 是否启用智能摆动拍摄
 * @param {DroneModel} droneModel - 无人机型号 ('3TD' 或 '4TD')
 * @returns {GenerateFlightPathOutput}
 */
export function generateObliqueFlightPath(
  polygonGeoJSON: Feature<Polygon>,
  altitude: number,
  gsd: number,
  frontalOverlap: number,
  sideOverlap: number,
  speed: number,
  angle: number,
  tiltAngle: number, // 相机与地面的夹角，90度为垂直
  bufferDistance: number,
  cameraParams: CameraParams,
  takeoffPoint: L.LatLng, // 新增起飞点参数，确保不为null
  isSmartObliqueEnabled: boolean, // 新增参数
  droneModel: DroneModel // 新增参数
): GenerateFlightPathOutput {
  console.log("--- 开始生成倾斜摄影航线 ---");
  console.log("输入参数:", {
    altitude,
    gsd,
    frontalOverlap,
    sideOverlap,
    speed,
    angle,
    tiltAngle,
    bufferDistance,
    cameraParams,
    takeoffPoint,
    isSmartObliqueEnabled, // 打印新参数
    droneModel, // 打印新参数
  });
  console.log("原始多边形 (GeoJSON):", JSON.stringify(polygonGeoJSON));

  let waypoints: Waypoint[] = [];
  const photoPoints: PhotoPoint[] = [];
  let totalDistance = 0;
  let totalPhotos = 0;

  const debugMidPoints: Feature<Point>[] = [];
  const debugMidPointsInPolygon: Feature<Point>[] = [];

  const defaultErrorResult = (errorMessage: string) => ({
    flightResult: {
      waypoints: [],
      photoPoints: [],
      stats: { duration: 0, distance: 0, photos: 0, area: 0 },
      error: errorMessage,
    },
    debugInfo: null,
  });

  // 0. 验证输入多边形
  if (!booleanValid(polygonGeoJSON)) {
    console.error("错误: 输入多边形无效。", polygonGeoJSON);
    return defaultErrorResult("输入的多边形无效，请检查绘制区域。");
  }

  // 1. & 2. 参数计算
  const pixelGroundSize = gsd / 100; // m/pixel
  const imageWidthGround = pixelGroundSize * cameraParams.IMAGE_WIDTH_PIXELS; // m
  const imageHeightGround = pixelGroundSize * cameraParams.IMAGE_HEIGHT_PIXELS; // m

  let currentLineSpacing = imageWidthGround * (1 - sideOverlap);
  let currentPhotoIntervalDistance = imageHeightGround * (1 - frontalOverlap);

  console.log("计算出的地面照片尺寸 (近似):", {
    imageWidthGround,
    imageHeightGround,
  });
  console.log(
    "计算出的航线间距:",
    currentLineSpacing,
    "拍照点间距:",
    currentPhotoIntervalDistance
  );

  if (currentLineSpacing <= 0 || currentPhotoIntervalDistance <= 0) {
    console.error("错误: 重叠度设置过高导致航线或拍照点间距为负或零。");
    return defaultErrorResult("重叠度设置过高导致航线或拍照点间距为负或零。");
  }

  // 3. 缓冲多边形
  const bufferedPolygon = buffer(polygonGeoJSON, bufferDistance, {
    units: "meters",
  });
  if (!bufferedPolygon || !booleanValid(bufferedPolygon)) {
    console.error("错误: 缓冲多边形生成失败或无效！", bufferedPolygon);
    return defaultErrorResult("缓冲区域无效，请检查原始区域。");
  }
  const totalArea = area(polygonGeoJSON); // Still report original area for stats
  console.log(
    "原始多边形面积:",
    totalArea,
    "缓冲后多边形:",
    JSON.stringify(bufferedPolygon)
  );

  const centroidPoint = centroid(bufferedPolygon); // Use centroid of buffered polygon for rotation
  console.log("缓冲多边形中心点:", JSON.stringify(centroidPoint));

  const rotationAngle = 90 - angle; // 航线方向与X轴平行
  console.log("用于旋转多边形的角度 (顺时针):", rotationAngle);

  const rotatedPolygonForScan = transformRotate(
    bufferedPolygon,
    rotationAngle,
    {
      pivot: centroidPoint,
    }
  );
  console.log(
    "旋转后的多边形 (rotatedPolygonForScan):",
    JSON.stringify(rotatedPolygonForScan)
  );
  if (!booleanValid(rotatedPolygonForScan)) {
    console.error("错误: 旋转后的多边形无效！", rotatedPolygonForScan);
    return defaultErrorResult("旋转后的区域无效，请检查原始区域。");
  }

  // 4. 生成扫描线 (在旋转后的缓冲多边形BBox内)
  const bboxRotated = bbox(rotatedPolygonForScan);
  const minXRotated = bboxRotated[0];
  const minYRotated = bboxRotated[1];
  const maxXRotated = bboxRotated[2];
  const maxYRotated = bboxRotated[3];

  const scanLines: Feature<LineString>[] = [];
  let currentY = minYRotated;
  // Use destination to calculate latitude increment more accurately
  const latitudeIncrementPerMeter =
    (1 /
      distance(
        point([centroidPoint.geometry.coordinates[0], currentY]),
        point([centroidPoint.geometry.coordinates[0], currentY + 0.01]),
        { units: "meters" }
      )) *
    0.01;

  while (currentY <= maxYRotated) {
    const lineCoords: Position[] = [
      [minXRotated, currentY],
      [maxXRotated, currentY],
    ];
    scanLines.push(lineString(lineCoords));
    currentY += currentLineSpacing * latitudeIncrementPerMeter; // 使用 currentLineSpacing
  }
  console.log("生成的扫描线数量:", scanLines.length);

  // 5. 裁剪航线到缓冲多边形内部
  const clippedLines: Feature<LineString>[] = [];
  const bufferedPolygonOuterRing = bufferedPolygon.geometry.coordinates[0];

  scanLines.forEach((line, lineIndex) => {
    const intersections: Position[] = [];
    for (let i = 0; i < bufferedPolygonOuterRing.length - 1; i++) {
      const p1 = bufferedPolygonOuterRing[i];
      const p2 = bufferedPolygonOuterRing[i + 1];
      const edge = lineString([p1, p2]);

      const intersectResult = lineIntersect(line, edge);
      if (intersectResult.features.length > 0) {
        intersectResult.features.forEach((feat) => {
          intersections.push(feat.geometry.coordinates);
        });
      }
    }

    const allPointsOnLine: Position[] = [
      line.geometry.coordinates[0],
      line.geometry.coordinates[1],
      ...intersections,
    ];
    allPointsOnLine.sort((a, b) => {
      if (a[0] !== b[0]) return a[0] - b[0];
      return a[1] - b[1];
    });

    const uniquePointsOnLine: Position[] = [];
    if (allPointsOnLine.length > 0) {
      uniquePointsOnLine.push(allPointsOnLine[0]);
      for (let i = 1; i < allPointsOnLine.length; i++) {
        const prev = uniquePointsOnLine[uniquePointsOnLine.length - 1];
        const curr = allPointsOnLine[i];
        if (
          distance(point(prev), point(curr), {
            units: "meters",
          }) > 0.01
        ) {
          uniquePointsOnLine.push(curr);
        }
      }
    }

    for (let i = 0; i < uniquePointsOnLine.length - 1; i++) {
      const startPos = uniquePointsOnLine[i];
      const endPos = uniquePointsOnLine[i + 1];

      if (getDistance(startPos, endPos) < 0.1) continue;

      const mid = midpoint(point(startPos), point(endPos));
      debugMidPoints.push(mid);
      const isInPolygon = booleanPointInPolygon(mid, bufferedPolygon); // Check against the buffered polygon
      if (isInPolygon) {
        clippedLines.push(lineString([startPos, endPos]));
        debugMidPointsInPolygon.push(mid);
      }
    }
  });
  console.log("裁剪后的线段数量 (clippedLines):", clippedLines.length);

  // 6. 将裁剪后的航线段旋转回原始角度
  const finalLines = clippedLines.map((line) =>
    transformRotate(line, -rotationAngle, { pivot: centroidPoint })
  );
  console.log("最终航线段数量 (finalLines):", finalLines.length);

  // 7. & 8. 整合航线、生成航点和统计信息
  const flightResult: FlightRouteResult = {
    waypoints: [],
    photoPoints: [],
    stats: { duration: 0, distance: 0, photos: 0, area: totalArea },
    error: "区域过小或参数不合理，未能生成有效航线。",
  };

  if (finalLines.length === 0) {
    console.error("错误: finalLines 为空，未能生成有效航线。");
    return {
      flightResult: flightResult,
      debugInfo: {
        rotatedPolygonForScan: rotatedPolygonForScan,
        bufferedPolygonForScan: bufferedPolygon,
        scanLines: scanLines,
        midPoints: debugMidPoints,
        midPointsInPolygon: debugMidPointsInPolygon,
      },
    };
  }

  // --- 航线优化开始 (与建图航拍类似) ---
  // 确保 finalLines 按照距离起飞点的最小距离进行排序
  finalLines.sort((lineA, lineB) => {
    const takeoffPointPosition: Position = [takeoffPoint.lng, takeoffPoint.lat];

    const startPosA = lineA.geometry.coordinates[0];
    const endPosA =
      lineA.geometry.coordinates[lineA.geometry.coordinates.length - 1];
    const distToStartA = getDistance(takeoffPointPosition, startPosA);
    const distToEndA = getDistance(takeoffPointPosition, endPosA);
    const minDistanceA = Math.min(distToStartA, distToEndA);

    const startPosB = lineB.geometry.coordinates[0];
    const endPosB =
      lineB.geometry.coordinates[lineB.geometry.coordinates.length - 1];
    const distToStartB = getDistance(takeoffPointPosition, startPosB);
    const distToEndB = getDistance(takeoffPointPosition, endPosB);
    const minDistanceB = Math.min(distToStartB, distToEndB);

    return minDistanceA - minDistanceB;
  });

  let missionEntryWaypoint: Waypoint | null = null;
  let minDistanceToTakeoff = Infinity;
  let initialLineIndex = -1;
  let initialLineTraversalStartsAtStart = false; // true: start->end, false: end->start

  const takeoffPointPosition: Position = [takeoffPoint.lng, takeoffPoint.lat];

  finalLines.forEach((line, index) => {
    if (
      !line ||
      !Array.isArray(line.geometry.coordinates) ||
      line.geometry.coordinates.length < 2
    ) {
      console.warn(
        `DEBUG: Skipping invalid line ${index} in closest point search:`,
        line
      );
      return;
    }

    const startPos = line.geometry.coordinates[0];
    const endPos =
      line.geometry.coordinates[line.geometry.coordinates.length - 1];

    if (
      !Array.isArray(startPos) ||
      startPos.length !== 2 ||
      typeof startPos[0] !== "number" ||
      isNaN(startPos[0]) ||
      typeof startPos[1] !== "number" ||
      isNaN(startPos[1])
    ) {
      console.warn(
        `DEBUG: Skipping invalid startPos for line ${index}:`,
        startPos
      );
      return;
    }
    if (
      !Array.isArray(endPos) ||
      endPos.length !== 2 ||
      typeof endPos[0] !== "number" ||
      isNaN(endPos[0]) ||
      typeof endPos[1] !== "number" ||
      isNaN(endPos[1])
    ) {
      console.warn(`DEBUG: Skipping invalid endPos for line ${index}:`, endPos);
      return;
    }

    const distToStart = getDistance(takeoffPointPosition, startPos);
    if (distToStart < minDistanceToTakeoff) {
      minDistanceToTakeoff = distToStart;
      missionEntryWaypoint = {
        lat: startPos[1],
        lng: startPos[0],
        alt: altitude,
      };
      initialLineIndex = index;
      initialLineTraversalStartsAtStart = true;
    }

    const distToEnd = getDistance(takeoffPointPosition, endPos);
    if (distToEnd < minDistanceToTakeoff) {
      minDistanceToTakeoff = distToEnd;
      missionEntryWaypoint = { lat: endPos[1], lng: endPos[0], alt: altitude };
      initialLineIndex = index;
      initialLineTraversalStartsAtStart = false;
    }
  });

  if (!missionEntryWaypoint) {
    console.error("Error: 无法确定任务入口航点。");
    return defaultErrorResult("无法确定任务入口航点，请检查起飞点和区域。");
  }

  // `lastWaypointForDistance` 现在从 `missionEntryWaypoint` 开始，因为起飞点不是任务航线的一部分
  let lastWaypointForDistance: Position = [
    missionEntryWaypoint!.lng,
    missionEntryWaypoint!.lat,
  ];
  waypoints.push(missionEntryWaypoint!); // 将最近的入口点作为第一个任务航点

  let initialLineReverseFlag = !initialLineTraversalStartsAtStart;

  const orderedMissionLines: { line: Feature<LineString>; reverse: boolean }[] =
    [];

  for (let i = 0; i < finalLines.length; i++) {
    let reverseThisLine: boolean;
    if (i === initialLineIndex) {
      reverseThisLine = initialLineReverseFlag;
    } else {
      if (Math.abs(i - initialLineIndex) % 2 === 0) {
        reverseThisLine = initialLineReverseFlag;
      } else {
        reverseThisLine = !initialLineReverseFlag;
      }
    }
    orderedMissionLines.push({ line: finalLines[i], reverse: reverseThisLine });
  }

  // 辅助函数：将一条线段的航点和拍照点添加到总路径中
  // 根据智能摆动拍摄模式和无人机型号，生成不同的拍照点
  const generateSegmentPointsWithObliqueLogic = (
    line: Feature<LineString>,
    reverse: boolean,
    currentAltitude: number,
    currentAngle: number, // 航线角度
    currentTilt: number, // 相机与地面的夹角 (90为垂直)
    photoInterval: number, // 拍照点间距
    isSmartOblique: boolean,
    model: DroneModel
  ): {
    segmentWaypoints: Waypoint[];
    segmentPhotoPoints: PhotoPoint[];
    segmentDistance: number;
  } => {
    let segmentWaypoints: Waypoint[] = [];
    let segmentPhotoPoints: PhotoPoint[] = [];
    let segmentDistance = 0;
    let currentSegmentPos: Position | null = null;

    let lineCoords = [...line.geometry.coordinates];
    if (reverse) {
      lineCoords.reverse();
    }

    const isValidLineCoords =
      Array.isArray(lineCoords) &&
      lineCoords.length >= 2 &&
      lineCoords.every(
        (coord) =>
          Array.isArray(coord) &&
          coord.length === 2 &&
          typeof coord[0] === "number" &&
          !isNaN(coord[0]) &&
          typeof coord[1] === "number" &&
          !isNaN(coord[1])
      );

    if (!isValidLineCoords) {
      console.error(
        "generateSegmentPointsWithObliqueLogic: Invalid or malformed lineCoords detected, skipping segment:",
        lineCoords,
        line
      );
      return {
        segmentWaypoints: [],
        segmentPhotoPoints: [],
        segmentDistance: 0,
      };
    }

    const turfLine = lineString(lineCoords);
    const lineLen = length(turfLine, { units: "meters" });

    const stepDistance = photoInterval > 0 ? photoInterval : 5; // 航点/拍照点密度，至少5米

    // 对于 3TD 模拟摆动，即使线段很短，也只生成一个航点，但会生成多个拍照点用于前端可视化
    // 实际 KMZ 会生成多条航线，每条航线一个固定角度。
    // 因此这里 photoPoints 的生成逻辑，仅用于前端可视化。
    if (lineLen < stepDistance / 2) {
      const p = lineCoords[0];
      segmentWaypoints.push({ lat: p[1], lng: p[0], alt: currentAltitude });
      if (isSmartOblique && model === "3TD") {
        // 模拟5向拍摄 (Nadir, 0, 90, 180, 270)
        segmentPhotoPoints.push({
          lat: p[1],
          lng: p[0],
          alt: currentAltitude,
          heading: 0,
          tilt: 90,
        }); // Nadir
        segmentPhotoPoints.push({
          lat: p[1],
          lng: p[0],
          alt: currentAltitude,
          heading: 0,
          tilt: currentTilt,
        }); // Front
        segmentPhotoPoints.push({
          lat: p[1],
          lng: p[0],
          alt: currentAltitude,
          heading: 90,
          tilt: currentTilt,
        }); // Right
        segmentPhotoPoints.push({
          lat: p[1],
          lng: p[0],
          alt: currentAltitude,
          heading: 180,
          tilt: currentTilt,
        }); // Back
        segmentPhotoPoints.push({
          lat: p[1],
          lng: p[0],
          alt: currentAltitude,
          heading: 270,
          tilt: currentTilt,
        }); // Left
      } else {
        segmentPhotoPoints.push({
          lat: p[1],
          lng: p[0],
          alt: currentAltitude,
          heading: currentAngle,
          tilt: currentTilt,
        });
      }
      return { segmentWaypoints, segmentPhotoPoints, segmentDistance: lineLen };
    }

    for (let d = 0; d <= lineLen + 0.01; d += stepDistance) {
      const pointAlongLine = along(turfLine, Math.min(d, lineLen), {
        units: "meters",
      });
      const currentPointPos: Position = pointAlongLine.geometry.coordinates;

      if (
        !Array.isArray(currentPointPos) ||
        currentPointPos.length !== 2 ||
        typeof currentPointPos[0] !== "number" ||
        isNaN(currentPointPos[0]) ||
        typeof currentPointPos[1] !== "number" ||
        isNaN(currentPointPos[1])
      ) {
        console.warn(
          "generateSegmentPointsWithObliqueLogic: 'along' returned invalid coordinates, skipping point:",
          pointAlongLine
        );
        continue;
      }

      if (
        currentSegmentPos &&
        getDistance(currentPointPos, currentSegmentPos) > 0.1
      ) {
        segmentDistance += getDistance(currentSegmentPos, currentPointPos);
      }
      segmentWaypoints.push({
        lat: currentPointPos[1],
        lng: currentPointPos[0],
        alt: currentAltitude,
      });

      // 根据智能摆动拍摄模式和无人机型号生成拍照点 (仅用于前端可视化)
      if (isSmartOblique && model === "3TD") {
        // 模拟5向拍摄 (Nadir, 0, 90, 180, 270)
        segmentPhotoPoints.push({
          lat: currentPointPos[1],
          lng: currentPointPos[0],
          alt: currentAltitude,
          heading: 0,
          tilt: 90,
        }); // Nadir
        segmentPhotoPoints.push({
          lat: currentPointPos[1],
          lng: currentPointPos[0],
          alt: currentAltitude,
          heading: 0,
          tilt: currentTilt,
        }); // Front
        segmentPhotoPoints.push({
          lat: currentPointPos[1],
          lng: currentPointPos[0],
          alt: currentAltitude,
          heading: 90,
          tilt: currentTilt,
        }); // Right
        segmentPhotoPoints.push({
          lat: currentPointPos[1],
          lng: currentPointPos[0],
          alt: currentAltitude,
          heading: 180,
          tilt: currentTilt,
        }); // Back
        segmentPhotoPoints.push({
          lat: currentPointPos[1],
          lng: currentPointPos[0],
          alt: currentAltitude,
          heading: 270,
          tilt: currentTilt,
        }); // Left
      } else {
        // 传统倾斜或4TD自带，只生成一个拍照点
        segmentPhotoPoints.push({
          lat: currentPointPos[1],
          lng: currentPointPos[0],
          alt: currentAltitude,
          heading: currentAngle, // 使用航线角度作为拍照方向
          tilt: currentTilt,
        });
      }
      currentSegmentPos = currentPointPos;
    }
    return { segmentWaypoints, segmentPhotoPoints, segmentDistance };
  };

  // 现在根据 orderedMissionLines 生成最终的航点
  for (let i = 0; i < orderedMissionLines.length; i++) {
    const { line, reverse } = orderedMissionLines[i];
    const { segmentWaypoints, segmentPhotoPoints, segmentDistance } =
      generateSegmentPointsWithObliqueLogic(
        line,
        reverse,
        altitude,
        angle, // 航线角度
        tiltAngle, // 相机倾斜角
        currentPhotoIntervalDistance, // 拍照点间距
        isSmartObliqueEnabled,
        droneModel
      );

    // 添加连接线（如果需要）
    const firstSegmentPoint = segmentWaypoints[0];
    if (
      firstSegmentPoint &&
      getDistance(lastWaypointForDistance, [
        firstSegmentPoint.lng,
        firstSegmentPoint.lat,
      ]) > 0.1
    ) {
      totalDistance += getDistance(lastWaypointForDistance, [
        firstSegmentPoint.lng,
        firstSegmentPoint.lat,
      ]);
      waypoints.push(firstSegmentPoint);
      lastWaypointForDistance = [firstSegmentPoint.lng, firstSegmentPoint.lat];
    }

    // 添加线段内部的航点和拍照点
    // 确保第一个点不重复添加
    const pointsToAdd =
      waypoints.length > 0 &&
      firstSegmentPoint &&
      getDistance(
        [
          waypoints[waypoints.length - 1].lng,
          waypoints[waypoints.length - 1].lat,
        ],
        [firstSegmentPoint.lng, firstSegmentPoint.lat]
      ) < 0.1
        ? segmentWaypoints.slice(1)
        : segmentWaypoints;

    waypoints.push(...pointsToAdd);
    photoPoints.push(...segmentPhotoPoints);
    totalDistance += segmentDistance;
    if (segmentWaypoints.length > 0) {
      lastWaypointForDistance = [
        segmentWaypoints[segmentWaypoints.length - 1].lng,
        segmentWaypoints[segmentWaypoints.length - 1].lat,
      ];
    }
  }

  // 计算往返起飞点的距离 (如果任务航点存在)
  let distanceToFirstMissionWaypoint = 0;
  let distanceFromLastMissionWaypoint = 0;
  if (waypoints.length > 0) {
    distanceToFirstMissionWaypoint = getDistance(takeoffPointPosition, [
      waypoints[0].lng,
      waypoints[0].lat,
    ]);
    distanceFromLastMissionWaypoint = getDistance(
      [
        waypoints[waypoints.length - 1].lng,
        waypoints[waypoints.length - 1].lat,
      ],
      takeoffPointPosition
    );
  }

  // 最终去重：移除所有连续的、距离过近的航点
  const finalWaypoints: Waypoint[] = [];
  if (waypoints.length > 0) {
    finalWaypoints.push(waypoints[0]);
    for (let i = 1; i < waypoints.length; i++) {
      const prev = finalWaypoints[finalWaypoints.length - 1];
      const curr = waypoints[i];
      if (getDistance([prev.lng, prev.lat], [curr.lng, curr.lat]) > 0.1) {
        // 0.1米阈值
        finalWaypoints.push(curr);
      } else {
        console.log("Removed duplicate waypoint:", curr); // 调试日志
      }
    }
  }

  // 重新计算总距离基于去重后的航点 (包含往返起飞点)
  let recalculatedTotalDistance = 0;
  // 计算从起飞点到第一个任务航点的距离
  if (finalWaypoints.length > 0) {
    recalculatedTotalDistance += getDistance(
      [takeoffPoint.lng, takeoffPoint.lat],
      [finalWaypoints[0].lng, finalWaypoints[0].lat]
    );
  }
  for (let i = 0; i < finalWaypoints.length - 1; i++) {
    recalculatedTotalDistance += getDistance(
      [finalWaypoints[i].lng, finalWaypoints[i].lat],
      [finalWaypoints[i + 1].lng, finalWaypoints[i + 1].lat]
    );
  }
  // 计算从最后一个任务航点到起飞点的距离
  if (finalWaypoints.length > 0) {
    recalculatedTotalDistance += getDistance(
      [
        finalWaypoints[finalWaypoints.length - 1].lng,
        finalWaypoints[finalWaypoints.length - 1].lat,
      ],
      [takeoffPoint.lng, takeoffPoint.lat]
    );
  }

  flightResult.waypoints = finalWaypoints;
  flightResult.photoPoints = photoPoints; // Photo points are generated at the same density as waypoints
  flightResult.stats.duration =
    speed > 0 ? recalculatedTotalDistance / speed : 0;
  flightResult.stats.distance = recalculatedTotalDistance;
  flightResult.stats.photos = photoPoints.length; // Count actual photo points
  flightResult.stats.area = totalArea;
  flightResult.error = undefined;

  return {
    flightResult: flightResult,
    debugInfo: {
      rotatedPolygonForScan: rotatedPolygonForScan,
      bufferedPolygonForScan: bufferedPolygon,
      scanLines: scanLines,
      midPoints: debugMidPoints,
      midPointsInPolygon: debugMidPointsInPolygon,
    },
  };
}

//用于 KMZ 导出时传递所有必要参数
export interface ExportParams {
  missionType: MissionType;
  mappingParams?: {
    altitude: number;
    gsd: number;
    frontalOverlap: number;
    sideOverlap: number;
    speed: number;
    angle: number;
  };
  obliqueParams?: {
    altitude: number;
    gsd: number;
    frontalOverlap: number;
    sideOverlap: number;
    speed: number;
    angle: number;
    tiltAngle: number;
    buffer: number;
  };
  drawnPolygonGeoJSON: Feature<Polygon>;
  takeoffPoint: L.LatLng; // 起飞点，现在确保不为 null
}

export function generateWaylinesWpml(
  flightRouteResult: FlightRouteResult,
  exportParams: ExportParams
): string {
  const { waypoints, stats } = flightRouteResult; // waypoints 现在只包含任务航点
  const { missionType, takeoffPoint } = exportParams;

  const currentParams =
    exportParams.mappingParams || exportParams.obliqueParams;
  if (!currentParams) {
    throw new Error("Missing mission parameters for WPML generation.");
  }

  const speed = currentParams.speed;
  const missionAngle = currentParams.angle; // 任务全局航线角度

  const initialGimbalPitch =
    missionType === "mapping"
      ? -90
      : -(exportParams.obliqueParams?.tiltAngle || 90);

  let waypointPlacemarksXml = "";
  const lastWaypointIndex = waypoints.length - 1; // 实际任务航线列表的最后一个索引

  // 计算拍照时间间隔 (minShootInterval)
  let minShootIntervalValue: number | null = null;
  if (currentParams.gsd && currentParams.frontalOverlap && speed > 0) {
    const pixelGroundSize = currentParams.gsd / 100; // m/pixel
    const imageHeightGround = pixelGroundSize * CAMERA_IMAGE_HEIGHT_PIXELS; // m
    const photoIntervalDistance =
      imageHeightGround * (1 - currentParams.frontalOverlap / 100); // m
    minShootIntervalValue = photoIntervalDistance / speed;
  }

  // 为每个任务航点生成 Placemark XML (waypoints 数组现在只包含任务航点)
  for (let i = 0; i < waypoints.length; i++) {
    const wp = waypoints[i];
    let headingAngle = missionAngle; // 默认使用任务全局角度 (0-180)

    // 计算航点之间的航向角
    if (i < waypoints.length - 1) {
      const nextWp = waypoints[i + 1];
      headingAngle = bearing(
        point([wp.lng, wp.lat]),
        point([nextWp.lng, nextWp.lat])
      );
    } else if (i > 0) {
      // 最后一个航点，使用前一个航段的航向角
      const prevWp = waypoints[i - 1];
      headingAngle = bearing(
        point([prevWp.lng, prevWp.lat]),
        point([wp.lng, wp.lat])
      );
    }
    // 如果只有一个航点，航向角默认为任务角度
    if (waypoints.length === 1) {
      headingAngle = missionAngle;
    }

    waypointPlacemarksXml += generateWaypointPlacemarkXml(
      wp,
      i,
      speed,
      headingAngle,
      lastWaypointIndex, // 传递最后一个任务航点的索引
      minShootIntervalValue,
      initialGimbalPitch // 传递云台俯仰角
    );
  }

  return `<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:wpml="http://www.dji.com/wpmz/1.0.6">
  <Document>
    <wpml:missionConfig>
      <wpml:flyToWaylineMode>safely</wpml:flyToWaylineMode>
      <wpml:finishAction>goHome</wpml:finishAction>
      <wpml:exitOnRCLost>goContinue</wpml:exitOnRCLost>
      <wpml:executeRCLostAction>goBack</wpml:executeRCLostAction>
      <wpml:takeOffSecurityHeight>20</wpml:takeOffSecurityHeight>
      <wpml:globalTransitionalSpeed>15</wpml:globalTransitionalSpeed>
      <wpml:globalRTHHeight>100</wpml:globalRTHHeight>
      <wpml:droneInfo>
        <wpml:droneEnumValue>100</wpml:droneEnumValue>
        <wpml:droneSubEnumValue>1</wpml:droneSubEnumValue>
      </wpml:droneInfo>
      <wpml:waylineAvoidLimitAreaMode>0</wpml:waylineAvoidLimitAreaMode>
      <wpml:payloadInfo>
        <wpml:payloadEnumValue>99</wpml:payloadEnumValue>
        <wpml:payloadSubEnumValue>0</wpml:payloadSubEnumValue>
        <wpml:payloadPositionIndex>0</wpml:payloadPositionIndex>
      </wpml:payloadInfo>
    </wpml:missionConfig>
    <Folder>
      <wpml:templateId>0</wpml:templateId>
      <wpml:executeHeightMode>WGS84</wpml:executeHeightMode>
      <wpml:waylineId>0</wpml:waylineId>
      <wpml:distance>${stats.distance.toFixed(6)}</wpml:distance>
      <wpml:duration>${stats.duration.toFixed(6)}</wpml:duration>
      <wpml:autoFlightSpeed>${speed.toFixed(3)}</wpml:autoFlightSpeed>
      <wpml:startActionGroup>
        <wpml:action>
          <wpml:actionId>0</wpml:actionId>
          <wpml:actionActuatorFunc>gimbalRotate</wpml:actionActuatorFunc>
          <wpml:actionActuatorFuncParam>
            <wpml:gimbalHeadingYawBase>aircraft</wpml:gimbalHeadingYawBase>
            <wpml:gimbalRotateMode>absoluteAngle</wpml:gimbalRotateMode>
            <wpml:gimbalPitchRotateEnable>1</wpml:gimbalPitchRotateEnable>
            <wpml:gimbalPitchRotateAngle>${initialGimbalPitch.toFixed(
              0
            )}</wpml:gimbalPitchRotateAngle>
            <wpml:gimbalRollRotateEnable>0</wpml:gimbalRollRotateEnable>
            <wpml:gimbalRollRotateAngle>0</wpml:gimbalRollRotateAngle>
            <wpml:gimbalYawRotateEnable>1</wpml:gimbalYawRotateEnable>
            <wpml:gimbalYawRotateAngle>0</wpml:gimbalYawRotateAngle>
            <wpml:gimbalRotateTimeEnable>0</wpml:gimbalRotateTimeEnable>
            <wpml:gimbalRotateTime>10</wpml:gimbalRotateTime>
            <wpml:payloadPositionIndex>0</wpml:payloadPositionIndex>
          </wpml:actionActuatorFuncParam>
        </wpml:action>
        <wpml:action>
          <wpml:actionId>1</wpml:actionId>
          <wpml:actionActuatorFunc>hover</wpml:actionActuatorFunc>
          <wpml:actionActuatorFuncParam>
            <wpml:hoverTime>0.5</wpml:hoverTime>
          </wpml:actionActuatorFuncParam>
        </wpml:action>
        <wpml:action>
          <wpml:actionId>2</wpml:actionId>
          <wpml:actionActuatorFunc>setFocusType</wpml:actionActuatorFunc>
          <wpml:actionActuatorFuncParam>
            <wpml:cameraFocusType>manual</wpml:cameraFocusType>
            <wpml:payloadPositionIndex>0</wpml:payloadPositionIndex>
          </wpml:actionActuatorFuncParam>
        </wpml:action>
        <wpml:action>
          <wpml:actionId>3</wpml:actionId>
          <wpml:actionActuatorFunc>focus</wpml:actionActuatorFunc>
          <wpml:actionActuatorFuncParam>
            <wpml:focusX>0</wpml:focusX>
            <wpml:focusY>0</wpml:focusY>
            <wpml:focusRegionWidth>0</wpml:focusRegionWidth>
            <wpml:focusRegionHeight>0</wpml:focusRegionHeight>
            <wpml:isPointFocus>0</wpml:isPointFocus>
            <wpml:isInfiniteFocus>1</wpml:isInfiniteFocus>
            <wpml:payloadPositionIndex>0</wpml:payloadPositionIndex>
            <wpml:isCalibrationFocus>0</wpml:isCalibrationFocus>
          </wpml:actionActuatorFuncParam>
        </wpml:action>
        <wpml:action>
          <wpml:actionId>4</wpml:actionId>
          <wpml:actionActuatorFunc>hover</wpml:actionActuatorFunc>
          <wpml:actionActuatorFuncParam>
            <wpml:hoverTime>1</wpml:hoverTime>
          </wpml:actionActuatorFuncParam>
        </wpml:action>
      </wpml:startActionGroup>
      ${waypointPlacemarksXml}
    </Folder>
  </Document>
</kml>`;
}

/**
 * 生成 template.kml 文件的内容
 * 严格遵循用户提供的template.kml模板格式
 * @param exportParams 导出所需的任务参数
 * @returns template.kml 文件的 XML 字符串
 */
export function generateTemplateKml(exportParams: ExportParams): string {
  const { missionType, drawnPolygonGeoJSON, takeoffPoint } = exportParams; // 解构 takeoffPoint

  const currentParams =
    exportParams.mappingParams || exportParams.obliqueParams;
  if (!currentParams) {
    throw new Error("Missing mission parameters for template KML generation.");
  }

  const altitude = currentParams.altitude;
  const speed = currentParams.speed;
  const angle = currentParams.angle; // 任务全局航线角度
  const frontalOverlap = currentParams.frontalOverlap; // 百分比
  const sideOverlap = currentParams.sideOverlap; // 百分比

  const templateType = missionType === "mapping" ? "mapping2d" : "oblique";
  const smartObliqueEnable = missionType === "oblique" ? 1 : 0;

  // 将绘制的多边形坐标转换为 KML 格式 (经度,纬度,海拔)
  const polygonCoords = drawnPolygonGeoJSON.geometry.coordinates[0]
    .map((coord) => `${coord[0].toFixed(6)},${coord[1].toFixed(6)},0`) // 假设地面海拔为0
    .join("\n                ");

  // wpml:direction 示例为 256，表示 0-360 度。我们使用任务角度。
  // 考虑到 template.kml 示例中的 256，这里保留原样，不进行 [-180, 180] 转换。
  // 如果 UI 角度是 0-180，那么它自然在 0-360 范围内。
  const kmlDirection = angle;

  const currentTime = new Date().getTime();

  return `<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:wpml="http://www.dji.com/wpmz/1.0.6">
  <Document>
    <wpml:author>xmyyzy123@qq.com</wpml:author>
    <wpml:createTime>${currentTime}</wpml:createTime>
    <wpml:updateTime>${currentTime}</wpml:updateTime>
    <wpml:missionConfig>
      <wpml:flyToWaylineMode>safely</wpml:flyToWaylineMode>
      <wpml:finishAction>goHome</wpml:finishAction>
      <wpml:exitOnRCLost>goContinue</wpml:exitOnRCLost>
      <wpml:executeRCLostAction>goBack</wpml:executeRCLostAction>
      <wpml:takeOffSecurityHeight>20</wpml:takeOffSecurityHeight>
      <wpml:takeOffRefPoint>${takeoffPoint.lat.toFixed(
        6
      )},${takeoffPoint.lng.toFixed(6)},0.000000</wpml:takeOffRefPoint>
      <wpml:takeOffRefPointAGLHeight>0</wpml:takeOffRefPointAGLHeight>
      <wpml:globalTransitionalSpeed>15</wpml:globalTransitionalSpeed>
      <wpml:globalRTHHeight>100</wpml:globalRTHHeight>
      <wpml:droneInfo>
        <wpml:droneEnumValue>100</wpml:droneEnumValue>
        <wpml:droneSubEnumValue>1</wpml:droneSubEnumValue>
      </wpml:droneInfo>
      <wpml:waylineAvoidLimitAreaMode>0</wpml:waylineAvoidLimitAreaMode>
      <wpml:payloadInfo>
        <wpml:payloadEnumValue>99</wpml:payloadEnumValue>
        <wpml:payloadSubEnumValue>0</wpml:payloadSubEnumValue>
        <wpml:payloadPositionIndex>0</wpml:payloadPositionIndex>
      </wpml:payloadInfo>
    </wpml:missionConfig>
    <Folder>
      <wpml:templateType>${templateType}</wpml:templateType>
      <wpml:templateId>0</wpml:templateId>
      <wpml:waylineCoordinateSysParam>
        <wpml:coordinateMode>WGS84</wpml:coordinateMode>
        <wpml:heightMode>EGM96</wpml:heightMode>
        <wpml:globalShootHeight>${altitude.toFixed(1)}</wpml:globalShootHeight>
        <wpml:surfaceFollowModeEnable>1</wpml:surfaceFollowModeEnable>
        <wpml:isRealtimeSurfaceFollow>0</wpml:isRealtimeSurfaceFollow>
        <wpml:surfaceRelativeHeight>${altitude.toFixed(
          1
        )}</wpml:surfaceRelativeHeight>
        <wpml:dsmFile>wpmz/res/dsm/wgs84_ASTGTMV003_N36E120_dem_3.tif</wpml:dsmFile>
      </wpml:waylineCoordinateSysParam>
      <wpml:autoFlightSpeed>${speed.toFixed(1)}</wpml:autoFlightSpeed>
      <Placemark>
        <wpml:caliFlightEnable>0</wpml:caliFlightEnable>
        <wpml:elevationOptimizeEnable>1</wpml:elevationOptimizeEnable>
        <wpml:smartObliqueEnable>${smartObliqueEnable}</wpml:smartObliqueEnable>
        <wpml:quickOrthoMappingEnable>0</wpml:quickOrthoMappingEnable>
        <wpml:facadeWaylineEnable>0</wpml:facadeWaylineEnable>
        <wpml:isLookAtSceneSet>0</wpml:isLookAtSceneSet>
        <wpml:shootType>time</wpml:shootType>
        <wpml:direction>${kmlDirection.toFixed(0)}</wpml:direction>
        <wpml:margin>0</wpml:margin>
        <wpml:efficiencyFlightModeEnable>0</wpml:efficiencyFlightModeEnable>
        <wpml:overlap>
          <wpml:orthoCameraOverlapH>${frontalOverlap.toFixed(
            0
          )}</wpml:orthoCameraOverlapH>
          <wpml:orthoCameraOverlapW>${sideOverlap.toFixed(
            0
          )}</wpml:orthoCameraOverlapW>
          <wpml:inclinedCameraOverlapH>${frontalOverlap.toFixed(
            0
          )}</wpml:inclinedCameraOverlapH>
          <wpml:inclinedCameraOverlapW>${sideOverlap.toFixed(
            0
          )}</wpml:inclinedCameraOverlapW>
        </wpml:overlap>
        <Polygon>
          <outerBoundaryIs>
            <LinearRing>
              <coordinates>
                ${polygonCoords}
              </coordinates>
            </LinearRing>
          </outerBoundaryIs>
        </Polygon>
        <wpml:ellipsoidHeight>${altitude.toFixed(1)}</wpml:ellipsoidHeight>
        <wpml:height>${altitude.toFixed(1)}</wpml:height>
      </Placemark>
      <wpml:payloadParam>
        <wpml:payloadPositionIndex>0</wpml:payloadPositionIndex>
        <wpml:focusMode>firstPoint</wpml:focusMode>
        <wpml:meteringMode>average</wpml:meteringMode>
        <wpml:returnMode>singleReturnStrongest</wpml:returnMode>
        <wpml:samplingRate>240000</wpml:samplingRate>
        <wpml:scanningMode>repetitive</wpml:scanningMode>
        <wpml:imageFormat>visable</wpml:imageFormat>
        <wpml:photoSize/>
      </wpml:payloadParam>
    </Folder>
  </Document>
</kml>`;
}
